#include "ModelItemCheckBoxDelegate.h"

#include <QApplication>
#include <QCheckBox>
#include <QDebug>
#include <QHBoxLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QStyleOptionButton>
#include <QStyleOptionFocusRect>

ModelItemCheckBoxDelegate::ModelItemCheckBoxDelegate(QObject *parent):
    QStyledItemDelegate(parent)
{

}

void ModelItemCheckBoxDelegate::commitAndCloseEditor()
{
    auto editor = qobject_cast<QWidget*>(sender())->parentWidget();
    emit commitData(editor);
    emit closeEditor(editor, QAbstractItemDelegate::NoHint);
}

void ModelItemCheckBoxDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (index.data(Qt::EditRole).canConvert<bool>())
    {
        bool checked = index.data(Qt::EditRole).toBool();

        QStyle *style = qApp->style();

        QRect checkBoxRect = style->subElementRect(QStyle::SE_CheckBoxIndicator, &option);
        int chkWidth = checkBoxRect.width();
        int chkHeight = checkBoxRect.height();
        int centerX = option.rect.left() + qMax(option.rect.width()/2-chkWidth/2, 0);
        int centerY = option.rect.top() + qMax(option.rect.height()/2-chkHeight/2, 0);

        QStyleOptionViewItem modifiedOption(option);
        modifiedOption.rect.moveTo(centerX, centerY);
        modifiedOption.rect.setSize(QSize(chkWidth, chkHeight));
        if(checked)
            modifiedOption.state |= QStyle::State_On;

        style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &modifiedOption, painter);
    }
    else
    {
        QStyledItemDelegate::paint(painter, option, index);
    }
}

QWidget *ModelItemCheckBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    if (index.data(Qt::EditRole).canConvert<bool>())
    {
        auto editor = new QWidget(parent);
        editor->setFocusPolicy(Qt::StrongFocus);
        editor->setContentsMargins(0, 0, 0, 0);
        editor->setAutoFillBackground(false);
        editor->setLayout(new QHBoxLayout);
        auto checkbox = new QCheckBox();
        editor->layout()->addWidget(checkbox);
        editor->layout()->setAlignment(checkbox, Qt::AlignCenter);
        editor->layout()->setContentsMargins(0, 0, 0, 0);
        editor->layout()->setSpacing(0);
        editor->setFocusProxy(checkbox);
        connect(checkbox, &QCheckBox::clicked,
                this, &ModelItemCheckBoxDelegate::commitAndCloseEditor);
        return editor;
    }
    else
    {
        return QStyledItemDelegate::createEditor(parent, option, index);
    }
}

void ModelItemCheckBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
    if (index.data(Qt::EditRole).canConvert<bool>())
    {
        auto checkbox = qobject_cast<QCheckBox*>(editor->layout()->itemAt(0)->widget());
        checkbox->setChecked(index.data().toBool());
    }
    else
    {
        return QStyledItemDelegate::setEditorData(editor, index);
    }
}

void ModelItemCheckBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
    if (index.data(Qt::EditRole).canConvert<bool>())
    {
        auto checkbox = qobject_cast<QCheckBox*>(editor->layout()->itemAt(0)->widget());
        model->setData(index, QVariant::fromValue(checkbox->isChecked()), Qt::EditRole);
    }
    else
    {
        QStyledItemDelegate::setModelData(editor, model, index);
    }
}

void ModelItemCheckBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    Q_UNUSED(option)
    Q_UNUSED(index)
    QSize size = editor->sizeHint();
    editor->setMinimumHeight(size.height());
    editor->setMinimumWidth(size.width());
    editor->setGeometry(option.rect);
}


bool ModelItemCheckBoxDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index)
{
    if (index.flags().testFlag(Qt::ItemIsEnabled) && index.data(Qt::EditRole).canConvert<bool>())
    {
        bool toggle = false;
        if (event->type() == QEvent::MouseButtonPress)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);            
            QStyle *style = qApp->style();
            QRect checkBoxRect = style->subElementRect(QStyle::SE_CheckBoxIndicator, &option);
            int chkWidth = checkBoxRect.width();
            int chkHeight = checkBoxRect.height();
            int centerX = option.rect.left() + qMax(option.rect.width()/2-chkWidth/2, 0);
            int centerY = option.rect.top() + qMax(option.rect.height()/2-chkHeight/2, 0);
            checkBoxRect.moveTo(centerX, centerY);
            if (mouseEvent->button() == Qt::LeftButton && checkBoxRect.contains(mouseEvent->pos()))
                toggle = true;
        }
        if (toggle)
        {
            event->accept();
            bool checked = model->data(index, Qt::EditRole).toBool();
            checked = !checked;
            model->setData(index, QVariant::fromValue(checked), Qt::EditRole);
            return true;
        }
    }

    return QStyledItemDelegate::editorEvent(event, model, option, index);
}
